#!/usr/bin/env python
#
# moviedb.py
#   This software is part of a movie DB suite
# Copyright (C) 2010 Kyran Owen-Mankovich

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
                                    
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

import cgi
import datetime
import imdb
import os

from google.appengine.api import users

from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app


### Global Variables
IMDb = imdb.IMDb(accessSystem="http", useModule="beautifulsoup")


### Helper Functions
def getActors (movie, limit=5):
  """getActors(movie[, limit=5]) -> string

  Returns a string list of up to limit actors, given an IMDb movie object.
  """
  return "; ".join([ person["canonical name"] for person in movie["actors"][0:limit] ])


def getReleaseDate (movie):
  """getReleaseDate(movie) -> datetime

  Returns a datetime object containing release date, given an IMDb movie object.
  """
  date = None
  IMDb.update(movie, info=("release dates",))

  for release in getVal(movie, "release dates"):
    try:
      if release.startswith("USA"):
        # Format is 'USA::8 September 1999 (Los Angeles, California) (premiere)'
        datestr = " ".join(release.split(" ")[0:3])
        date    = datetime.datetime.strptime(datestr, "USA::%d %B %Y")
        break
    except ValueError:
      # Parse error, move on to the next USA date.
      pass

  return date


def getTitle (movie):
  """getTitle(movie) -> string

  Returns a string containing title, given an IMDb movie object.
  """
  return getVal(movie, "smart long imdb canonical title")


def getVal (movie, key):
  """getVal(movie, key) -> string | "Unknown"

  Returns a movie[key] or "Unknown" if key does not exist in movie.
  """
  result = "Unknown"
  if key in movie.keys():
    result = movie[key]
  return result


def getVar (handler, name):
  """getVar(handler, name) -> string

  Returns the sanitized value of the given variable name.
  """
  return cgi.escape( handler.request.get(name) )


def parseDateTime (datestr):
  """parseDateTime(datestr) -> python datetime object

  Returns python datetime object
  """
  # Default value for result in case no format is matched
  result = None

  # Possible formats include the year as '99' and '1999'
  formats = ["%m/%d/%y","%m/%d/%Y"]

  for format in formats:
    try:
      result = datetime.datetime.strptime(datestr, format)
      break
    except ValueError:
      # Parse error, move on to the format
      pass
  return result


### Class Definitions
class Movie (db.Model):
  """Movie

  Sets up structure of DB
  """
  tt             = db.StringProperty()
  title          = db.StringProperty()
  releaseDate    = db.DateTimeProperty()
  genre          = db.StringProperty(multiline=True)
  actors         = db.StringProperty(multiline=True)
  plot           = db.TextProperty()
  seen           = db.DateTimeProperty()
  own            = db.StringProperty()
  amazon         = db.StringProperty()
  netflix        = db.StringProperty()
  recommendedBy  = db.StringProperty(multiline=True)
  review         = db.TextProperty()


# Maps title and IMDb ID
class TitleID ():
  def __init__(self, imdbid, title):
    """TitleID(imdbid, title) -> TitleID

    Creates a new TitleID given an IMDb id and movie title.
    """
    self.imdbid = imdbid
    self.title  = title


class LookupTitle (webapp.RequestHandler):
  """LookupTitle

  Defines and builds the title lookup page.
  """
  def get (self):
    results = []
    title   = getVar(self, "title")
    if title != "":
      for movie in IMDb.search_movie(title):
        results.append( TitleID(movie.movieID, getTitle(movie)) )

    movies = Movie.all()
    path = os.path.join(os.path.dirname(__file__), "lookup.html")
    self.response.out.write( template.render(path, locals()) )


class AddMovie (webapp.RequestHandler):
  """AddMovie

  Defines and builds the add page with a number of information pulled from IMDb.
  """
  def get (self):
    tt = getVar(self, "tt")
    if tt != "":
      movie       = IMDb.get_movie(tt)
      title       = getTitle(movie)
      releaseDate = getReleaseDate(movie)
      genre       = ", ".join(movie["genres"])
      actors      = getActors(movie)
      plot        = getVal(movie, "plot")

    if releaseDate is None:
      releaseDate = "Unknown"
    else:
      releaseDate = releaseDate.strftime("%m/%d/%Y")

    if type(plot) is list:
      plot = plot[0]

    # Fetchs current DB contents
    # movies_query = Movie.all().order("title")
    # movies = movies_query.fetch(10)

    path = os.path.join(os.path.dirname(__file__), "add.html")
    self.response.out.write( template.render(path, locals()) )


class UpdateDB (webapp.RequestHandler):
  """UpdateDB

  Adds all the fields of a given movie to the DB.
  """
  def post (self):
    movie = Movie()

    movie.tt            = getVar(self, "tt")
    # The following 5 items are pulled from IMDb
    movie.title         = getVar(self, "title")
    movie.releaseDate   = parseDateTime(getVar(self, "releaseDate"))
    movie.genre         = getVar(self, "genre")
    movie.actors        = getVar(self, "actors")
    movie.plot          = getVar(self, "plot")
    # The following 6 items are pulled from user input (aka the /add page)
    movie.seen          = parseDateTime(getVar(self, "seen"))
    movie.own           = getVar(self, "own")
    movie.amazon        = getVar(self, "amazon")
    movie.netflix       = getVar(self, "netflix")
    movie.recommendedBy = getVar(self, "recommendedBy")
    movie.review        = getVar(self, "review")

    movie.put()
    self.redirect("/")

class BatchUpdateDB (webapp.RequestHandler):
  """BatchUpdateDB

  Adds all the fields of from the text field to the DB.
  """
  def post (self):
    all = getVar(self, "all")
    self.response.out.write(all)


def main ():
  map = [
    ( "/"            , LookupTitle   ),
    ( "/add"         , AddMovie      ),
    ( "/update"      , UpdateDB      ),
    ( "/batchUpdate" , BatchUpdateDB )
  ]

  app = webapp.WSGIApplication(map, debug=True)
  run_wsgi_app(app)


if __name__ == "__main__":
    main()
